/*
 * Copyright (c) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include <securec.h>

#include "apdu_core_defines.h"
#include "response_apdu.h"
#include "response_apdu_inner.h"

namespace OHOS {
namespace SeBaseServices {
namespace UnitTest {
using namespace testing;
TEST(ResponseApduTest, SwMacro)
{
    EXPECT_EQ(SW1(0x9000), 0x90);
    EXPECT_EQ(SW2(0x9000), 0x00);

    EXPECT_EQ(SW1(0x6985), 0x69);
    EXPECT_EQ(SW2(0x6985), 0x85);
}

TEST(ResponseApduTest, CreateResponseApduInvalidSize)
{
    ResponseApdu *resApdu = CreateResponseApdu(UINT32_MAX);
    EXPECT_EQ(resApdu, nullptr);
}

TEST(ResponseApduTest, GetStatusWordsNullptr)
{
    EXPECT_FALSE(GetStatusWords(nullptr, nullptr));

    ResponseApdu *resApdu = CreateResponseApdu(sizeof(uint64_t));
    EXPECT_FALSE(GetStatusWords(resApdu, nullptr));
    DestroyResponseApdu(resApdu);
}

TEST(ResponseApduTest, GetStatusWordsNiceCase)
{
    ResponseApdu *resApdu = CreateResponseApdu(sizeof(uint64_t));
    ASSERT_NE(resApdu, nullptr);
    EXPECT_NE(resApdu->data, nullptr);
    EXPECT_EQ(resApdu->length, sizeof(uint64_t) + sizeof(uint16_t));

    uint8_t vector[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x90, 0x00};
    ASSERT_EQ(memcpy_s(resApdu->data, resApdu->length, vector, sizeof(vector) / sizeof(uint8_t)), EOK);

    uint16_t statusWord = 0;
    EXPECT_TRUE(GetStatusWords(resApdu, &statusWord));
    EXPECT_EQ(statusWord, SW_NO_ERROR);

    DestroyResponseApdu(resApdu);
}

TEST(ResponseApduTest, CheckStatusWordsNullptr)
{
    EXPECT_FALSE(CheckStatusWords(nullptr, SW_NO_ERROR));
}

TEST(ResponseApduTest, CheckStatusWords)
{
    ResponseApdu *resApdu = CreateResponseApdu(sizeof(uint64_t));
    ASSERT_NE(resApdu, nullptr);
    EXPECT_NE(resApdu->data, nullptr);
    EXPECT_EQ(resApdu->length, sizeof(uint64_t) + sizeof(uint16_t));

    uint8_t vector[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x90, 0x00};
    ASSERT_EQ(memcpy_s(resApdu->data, resApdu->length, vector, sizeof(vector) / sizeof(uint8_t)), EOK);

    EXPECT_TRUE(CheckStatusWords(resApdu, SW_NO_ERROR));

    EXPECT_FALSE(CheckStatusWords(resApdu, SW_NO_ERROR + 1));
    DestroyResponseApdu(resApdu);
}

TEST(ResponseApduTest, GetResponseUint32ValueAtWithNullptr)
{
    EXPECT_FALSE(GetResponseUint32ValueAt(nullptr, 0, nullptr));

    ResponseApdu *resApdu = CreateResponseApdu(sizeof(uint64_t));
    EXPECT_FALSE(GetResponseUint32ValueAt(resApdu, 0, nullptr));
    DestroyResponseApdu(resApdu);
}

TEST(ResponseApduTest, GetResponseUint32ValueAtWithOk)
{
    ResponseApdu *resApdu = CreateResponseApdu(sizeof(uint64_t));
    ASSERT_NE(resApdu, nullptr);
    EXPECT_NE(resApdu->data, nullptr);
    EXPECT_EQ(resApdu->length, sizeof(uint64_t) + sizeof(uint16_t));

    uint8_t vector[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x90, 0x00};
    ASSERT_EQ(memcpy_s(resApdu->data, resApdu->length, vector, sizeof(vector) / sizeof(uint8_t)), EOK);

    uint32_t value = 0;
    EXPECT_TRUE(GetResponseUint32ValueAt(resApdu, 0, &value));
    EXPECT_EQ(value, 0x11223344);
    EXPECT_TRUE(GetResponseUint32ValueAt(resApdu, sizeof(uint32_t), &value));
    EXPECT_EQ(value, 0x55667788);
    EXPECT_FALSE(GetResponseUint32ValueAt(resApdu, sizeof(uint32_t) + 1, &value));
    DestroyResponseApdu(resApdu);
}

TEST(ResponseApduTest, GetResponseUint32ValueAtWithToobigIndex)
{
    ResponseApdu *resApdu = CreateResponseApdu(sizeof(uint64_t));

    uint32_t value = 0;
    EXPECT_FALSE(GetResponseUint32ValueAt(resApdu, 2048, &value));
    DestroyResponseApdu(resApdu);
}

TEST(ResponseApduTest, GetResponseBufferValueAtCaseNullInput)
{
    EXPECT_FALSE(GetResponseBufferValueAt(nullptr, 0, nullptr, 0));

    ResponseApdu *resApdu = CreateResponseApdu(sizeof(uint64_t));
    EXPECT_FALSE(GetResponseBufferValueAt(resApdu, 0, nullptr, 0));
    DestroyResponseApdu(resApdu);
}

TEST(ResponseApduTest, GetResponseBufferValueAtNiceArray)
{
    ResponseApdu *resApdu = CreateResponseApdu(sizeof(uint64_t));
    ASSERT_NE(resApdu, nullptr);
    EXPECT_NE(resApdu->data, nullptr);
    EXPECT_EQ(resApdu->length, sizeof(uint64_t) + sizeof(uint16_t));

    uint8_t vector[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x90, 0x00};
    ASSERT_EQ(memcpy_s(resApdu->data, resApdu->length, vector, sizeof(vector) / sizeof(uint8_t)), EOK);

    uint8_t buffer[sizeof(uint64_t)] = {0};
    EXPECT_TRUE(GetResponseBufferValueAt(resApdu, 0, buffer, sizeof(uint64_t)));
    EXPECT_THAT(std::vector<uint8_t>(buffer, buffer + sizeof(uint64_t)),
        ElementsAreArray({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}));

    DestroyResponseApdu(resApdu);
}

TEST(ResponseApduTest, GetResponseBufferValueAtNiceArray2)
{
    ResponseApdu *resApdu = CreateResponseApdu(sizeof(uint64_t));
    ASSERT_NE(resApdu, nullptr);
    EXPECT_NE(resApdu->data, nullptr);
    EXPECT_EQ(resApdu->length, sizeof(uint64_t) + sizeof(uint16_t));

    uint8_t vector[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x90, 0x00};
    ASSERT_EQ(memcpy_s(resApdu->data, resApdu->length, vector, sizeof(vector) / sizeof(uint8_t)), EOK);

    uint8_t buffer[sizeof(uint64_t)] = {0};

    EXPECT_TRUE(GetResponseBufferValueAt(resApdu, sizeof(uint32_t), buffer, sizeof(uint32_t)));
    EXPECT_THAT(std::vector<uint8_t>(buffer, buffer + sizeof(uint32_t)), ElementsAreArray({0x55, 0x66, 0x77, 0x88}));

    DestroyResponseApdu(resApdu);
}

TEST(ResponseApduTest, GetResponseBufferValueAtArrayExceed)
{
    ResponseApdu *resApdu = CreateResponseApdu(sizeof(uint64_t));
    ASSERT_NE(resApdu, nullptr);
    EXPECT_NE(resApdu->data, nullptr);
    EXPECT_EQ(resApdu->length, sizeof(uint64_t) + sizeof(uint16_t));

    uint8_t vector[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x90, 0x00};
    ASSERT_EQ(memcpy_s(resApdu->data, resApdu->length, vector, sizeof(vector) / sizeof(uint8_t)), EOK);

    uint8_t buffer[sizeof(uint64_t)] = {0};

    EXPECT_FALSE(GetResponseBufferValueAt(resApdu, sizeof(uint32_t), buffer, 4096));
    EXPECT_FALSE(GetResponseBufferValueAt(resApdu, sizeof(uint32_t), buffer, 0));
    EXPECT_FALSE(GetResponseBufferValueAt(resApdu, sizeof(uint32_t), buffer, sizeof(uint32_t) + 1));

    EXPECT_FALSE(GetResponseBufferValueAt(resApdu, 4096, buffer, sizeof(uint32_t) + 1));
    DestroyResponseApdu(resApdu);
}

TEST(ResponseApduTest, GetResponseBufferValueAtCase3)
{
    ResponseApdu *resApdu = CreateResponseApdu(sizeof(uint8_t));
    ASSERT_NE(resApdu, nullptr);
    EXPECT_NE(resApdu->data, nullptr);
    EXPECT_EQ(resApdu->length, sizeof(uint8_t) + sizeof(uint16_t));

    uint8_t vector[] = {0x11, 0x90, 0x00};
    ASSERT_EQ(memcpy_s(resApdu->data, resApdu->length, vector, sizeof(vector) / sizeof(uint8_t)), EOK);

    uint8_t value = 0;

    EXPECT_TRUE(GetResponseBufferValueAt(resApdu, 0, &value, 1));
    EXPECT_EQ(value, 0x11);

    EXPECT_FALSE(GetResponseBufferValueAt(resApdu, 1, &value, 1));

    DestroyResponseApdu(resApdu);
}

TEST(ResponseApduTest, GetResponseDataBufferInvalidInput)
{
    EXPECT_EQ(GetResponseDataBuffer(nullptr, nullptr), nullptr);

    ResponseApdu *resApdu = CreateResponseApdu(sizeof(uint8_t));
    EXPECT_EQ(GetResponseDataBuffer(resApdu, nullptr), nullptr);

    DestroyResponseApdu(resApdu);
}

TEST(ResponseApduTest, GetResponseDataBuffer)
{
    ResponseApdu *resApdu = CreateResponseApdu(sizeof(uint8_t));
    ASSERT_NE(resApdu, nullptr);
    EXPECT_NE(resApdu->data, nullptr);
    EXPECT_EQ(resApdu->length, sizeof(uint8_t) + sizeof(uint16_t));

    uint32_t size = MAX_APDU_RESP_SIZE;

    EXPECT_NE(GetResponseDataBuffer(resApdu, &size), nullptr);
    EXPECT_EQ(size, sizeof(uint8_t));

    DestroyResponseApdu(resApdu);
}

TEST(ResponseApduTest, CreateResponseApduCaseInvalidInput)
{
    {
        EXPECT_FALSE(CheckResponseApdu(nullptr));
    }
    {
        ResponseApdu rsp = {.length = 0};
        EXPECT_FALSE(CheckResponseApdu(&rsp));
    }
    {
        ResponseApdu rsp = {.length = 4096};
        EXPECT_FALSE(CheckResponseApdu(&rsp));
    }
}

TEST(ResponseApduTest, DestroyResponseApduNullInput)
{
    // will not crash
    DestroyResponseApdu(nullptr);
}
} // namespace UnitTest
} // namespace SeBaseServices
} // namespace OHOS